package com.ashin.plugin;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.RoutingStatementHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;

import com.ashin.bean.Page;

@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class})})
public class PagePlugin implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        RoutingStatementHandler handler = (RoutingStatementHandler) invocation.getTarget();
        BoundSql boundSql = handler.getBoundSql();
        Object parameterObject = boundSql.getParameterObject();
        Page page = findPageParam(parameterObject);
        if (page == null) {
            return invocation.proceed();
        }

        String originSql = originSql(boundSql);

        // 查询总记录
        Connection connection = (Connection) invocation.getArgs()[0];
        ParameterHandler parameterHandler = handler.getParameterHandler();
        long totalCount = totalCount(connection, parameterHandler, originSql);

        page.setTotalRecNum(totalCount);

        String pageSql = pageSql(page, originSql);

        Field sqlField = boundSql.getClass().getDeclaredField("sql");
        sqlField.setAccessible(true);
        sqlField.set(boundSql, pageSql);

        return invocation.proceed();

    }

    private String pageSql(Page page, String originSql) {
        int limitValue = (page.getPageNo() - 1) * page.getPageSize();
        String pageSql = originSql + " limit " + limitValue + "," + page.getPageSize();
        return pageSql;
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {

    }

    /**
     * 查找是否有page参数，有则进行分页
     *
     * @param obj
     * @return
     */
    @SuppressWarnings({"rawtypes", "unchecked"})
    private Page findPageParam(Object obj) {
        if (obj instanceof Page) {
            return (Page) obj;

        } else if (obj instanceof MapperMethod.ParamMap) {
            MapperMethod.ParamMap paramMap = (MapperMethod.ParamMap) obj;
            Set<Map.Entry> entrySet = paramMap.entrySet();
            for (Map.Entry entry : entrySet) {
                if (entry.getValue() instanceof Page) {
                    return (Page) entry.getValue();
                }
            }
        }

        return null;
    }

    /**
     * 获取xml中的源sql语句
     *
     * @param boundSql
     * @return
     */
    private String originSql(BoundSql boundSql) {
        String sql = boundSql.getSql();
        String newSql = sql;
        if (sql.endsWith(";")) {
            newSql = sql.substring(0, sql.length() - 1);
        }

        return newSql;
    }

    private long totalCount(Connection connection, ParameterHandler parameterHandler, String sql) throws Exception {
        String countSql = "select count(*) as totalCount from ( " + sql + " ) t";
        PreparedStatement pstmt = connection.prepareStatement(countSql);
        parameterHandler.setParameters(pstmt);
        ResultSet rs = pstmt.executeQuery();
        if (rs.next()) {
            return rs.getLong("totalCount");
        }

        return 0L;
    }
}
